/*
 * WeatherDataBase
 *
 * Copyright 2008-2009 Centro Andaluz de Innovación y Tecnologías de la
 * Información y las Comunicaciones (CITIC). All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 *
 * Please contact CITIC - Edificio CITIC, C/ Marie Curie, 6 Parque
 * Tecnológico de Andalucía 29590 - Campanillas, Málaga, Spain
 * or visit www.citic.es, if you need additional information or
 * have any questions.
 */

package es.citic.puedes.weatherservice.impl.bbdd;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.Persistence;
import javax.persistence.Query;

import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import es.citic.puedes.weatherservice.WeatherException;
import es.citic.puedes.weatherservice.impl.tables.WeatherLocation;
import es.citic.puedes.weatherservice.impl.tables.WeatherUserModel;

public class WeatherDataBase {
	private static Logger logger = LoggerFactory
			.getLogger(WeatherDataBase.class);

	private EntityManagerFactory emf = null;
	private String persistenceUnit = null;
	private Map<String, String> dBproperties;

	/**
	 * Contructor.
	 * 
	 * @param persistenceUnit
	 *            persistenceUnit persistence unit name
	 */
	public WeatherDataBase(String persistenceUnit) {
		this(persistenceUnit, Collections.<String, String> emptyMap());
	}

	/**
	 * Contructor.
	 * 
	 * @param persistenceUnit
	 *            persistence unit name
	 * @param properties
	 *            db connection properties, example:
	 *            PersistenceUnitProperties.JDBC_DRIVER, "org.hsqldb.jdbcDriver"
	 */
	public WeatherDataBase(String persistenceUnit,
			Map<String, String> properties) {
		this.persistenceUnit = persistenceUnit;
		this.dBproperties = properties;
		this.emf = Persistence.createEntityManagerFactory(
				this.persistenceUnit, this.dBproperties);
	}

	public void install() {
		HashMap<String, Object> installProperties = new HashMap<String, Object>();
		installProperties.put(PersistenceUnitProperties.CLASSLOADER, this
				.getClass().getClassLoader());
		installProperties.put(PersistenceUnitProperties.DDL_GENERATION, "drop-and-create-tables");
		EntityManagerFactory emf2 = Persistence.createEntityManagerFactory(this.persistenceUnit,
				installProperties);
		EntityManager em = emf2.createEntityManager();
		em.close();
		emf2.close();
	}

	/**
	 * Closes the EntityManager after a database query is finished
	 * 
	 * @param em
	 *            EntityManager object to be closed
	 */
	public void closeEntityManager(EntityManager em) {
		logger.debug("Enter :closeEntityManager");
		if (em != null)
			em.close();
		/*if (this.emf != null)
			this.emf.close();*/
		logger.debug("Exit :closeEntityManager");
	}

	/**
	 * Gets the EntityManager for database queries Creates database tables
	 * 
	 * @return em the EntityManager
	 * @throws WeatherException
	 *             if any error getting the EntityManager appears
	 */
	public EntityManager getEntityManager() throws WeatherException {
		logger.debug("Enter :getEntityManager()");

//		try {
//			this.emf = Persistence.createEntityManagerFactory(
//					this.persistenceUnit, this.dBproperties);
//		} catch (Exception e) {
//			throw new WeatherException(
//					"WeatherException: error getting the EntityManager "
//							+ e.getMessage());
//		}
//		logger.debug("getEntityManager ends..");

		EntityManager em = this.emf.createEntityManager(this.dBproperties);
		logger.debug("getEntityManager ends..");
		return em;
	}

	/**
	 * Drop and create all tables.
	 */
	public synchronized void cleanEntityManager() {

		logger.info("Enter :cleanEntityManager()");

		if (this.emf != null && this.emf.isOpen()) {
			this.emf.close();
		}

		HashMap<String, String> properties = new HashMap<String, String>(
				this.dBproperties);
		properties.put(PersistenceUnitProperties.DDL_GENERATION,
				PersistenceUnitProperties.DROP_AND_CREATE);
		this.emf = Persistence.createEntityManagerFactory(this.persistenceUnit,
				properties);
		EntityManager entityManager = this.emf.createEntityManager();
		if (entityManager != null && entityManager.isOpen()) {
			entityManager.close();
			this.emf.close();
		}
		entityManager = null;
		this.emf = null;

		logger.info("Exit :cleanEntityManager()");

	}

	/**
	 * Gets the list of WeatherUserModel objects (Preference model) for a
	 * Weather user
	 * 
	 * @param em
	 *            EntityManager
	 * @param userId
	 *            user id
	 * @return userLocationList WeatherUserModel object list
	 * @throws WeatherException
	 *             if any error in the query appears
	 */
	@SuppressWarnings("unchecked")
	public List<WeatherUserModel> selectWeatherUserModelQuery(EntityManager em,
			Integer userId) throws WeatherException {
		logger.debug("Enter :selectWeatherUserModelQuery");
		List<WeatherUserModel> weatherUserModelQueryList = null;
		try {

			// Select all weatherusermodel from an user
			Query weatherUserModelQuery = em
					.createQuery(
							"SELECT uk FROM WeatherUserModel uk WHERE uk.userId = :userId")
					.setParameter("userId", userId);

			weatherUserModelQueryList = weatherUserModelQuery.getResultList();

		} catch (NoResultException nre) {
			logger.debug("no user WeatherModel for this user");
		} catch (Exception e) {
			logger.error(e.getMessage());
			throw new WeatherException("WeatherException: " + e.getMessage());
		}
		logger.debug("Exit :selectWeatherUserModelQuery");
		return weatherUserModelQueryList;
	}

	/**
	 * checks weather the keyword was selected by the current user previously
	 * 
	 * @param em
	 *            EntityManager object
	 * @param location
	 *            Location name
	 * @param country
	 *            Country name
	 * @return WeatherLocation WeatherLocation object
	 * @throws WeatherException
	 *             if any error in the query appears
	 */
	public WeatherLocation selectWeatherLocationQuery(EntityManager em,
			String location, String country) throws WeatherException {
		logger.debug("Enter :selectWeatherLocationQuery");
		WeatherLocation weatherLocation = null;
		try {

			// Select WeatherLocation from user
			Query weatherLocationQuery = em
					.createQuery(
							"SELECT k FROM WeatherLocation k WHERE k.location = :location and k.country = :country ")
					.setParameter("location", location)
					.setParameter("country", country);

			weatherLocation = (WeatherLocation) weatherLocationQuery
					.getSingleResult();

		} catch (NoResultException nre) {
			logger.debug("no user Weather for this location");
		} catch (Exception e) {
			logger.error(e.getMessage());
			throw new WeatherException("WeatherException: " + e.getMessage());
		}
		logger.debug("Exit :selectWeatherLocationQuery");
		return weatherLocation;
	}

}
